// import { useState } from 'react'
// import './index.css'
// function Deepseek() {
//     const [question, setQuestion] = useState('')//获得用户输入的问题
//     const [content, setContent] = useState('')//展示回答
//     const [streaming, setStreaming] = useState(false)
//     let str = ''
//     const update = async () => {
//         //获取用户在input中输入的文本
//         if (!question) return//如果question为空则返回
//         setContent('思考中...')
//         //跟deepdeek交互
//         const endpoint = 'https://api.deepseek.com/chat/completions'
//         const headers = {
//             'Content-Type': 'application/json',
//             "Authorization": `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`
//         }
//         const response = await fetch(endpoint, {
//             method: 'POST',
//             headers: headers,
//             body: JSON.stringify({
//                 model: "deepseek-chat",
//                 messages: [{ role: "user", content: question }],
//                 stream: streaming
//             })//将对象转换成json字符串
//         })
//         //非流式输出
//         // const data = await response.json()
//         // setContent(data.choices[0].message.content);//展示大模型的回答
//         //流式输出
//         if (streaming) {
//             const reader = response.body.getReader()//读取器
//             const decoder = new TextDecoder() //解码二进制数据
//             //解码长度取决于存储系统的位数，64位表示一次性只能解码64位，所以会分开解码
//             let done = false//表示解码是否完成
//             let buffer = ''
//             while (!done) {
//                 const { value, done } = await reader.read()//读取二进制数据
//                 //value表示读取到的二进制数据
//                 const chunkValue = buffer + decoder.decode(value)
//                 //将解码出来的数据变为字符串
//                 buffer = ''
//                 const lines = chunkValue.split('\n').filter(line => line.startsWith('data:'))
//                 //把字符串切割成数组,后过滤，再放到lines数组中
//                 for (const line of lines) {
//                     const incoming = line.slice(6)
//                     // 切数组内的元素，每次都从下标6开始切
//                     if (incoming === '[DONE]') {
//                         done = true
//                         break
//                     }
//                     try {
//                         const data = JSON.parse(incoming)//把切出来的字符串
//                         const delta = data.choices[0].delta.content
//                         if (delta) {
//                             setContent((pev) => {
//                                 return pev + delta
//                             })
//                         }
//                     } catch (err) {
//                         console.log(err);
//                     }
//                 }
//             }
//         } else {
//             const data = await response.json()
//             setContent(data.choices[0].message.content);
//         }
//     }
//     return (
//         <div className="container">
//             <div>
//                 <label htmlFor="">请输入:</label>
//                 <input type="text" className="input" onChange={(e) => {
//                     setQuestion(e.target.value)
//                 }} />
//                 <button onClick={update}>发送</button>
//             </div>
//             <div className="response">
//                 <div>
//                     <label htmlFor="">streaming</label>
//                     <input type="checkbox" onChange={(e) => {
//                         setStreaming(e.target.checked)
//                     }} />
//                 </div>
//                 <div>{content}</div>
//             </div>
//         </div>
//     )
// }
// export default Deepseek;


import { useState } from 'react'
import './index.css'

function Deepseek() {
    const [question, setQuestion] = useState('') // 获得用户输入的问题
    const [content, setContent] = useState('') // 展示回答
    const [streaming, setStreaming] = useState(false)//表示是否为输入流

    const update = async () => {
        // 获取用户在 input 中输入的文本
        if (!question) return // 如果 question 为空则返回
        setContent('思考中...')

        // 跟 deepdeek 交互
        const endpoint = 'https://api.deepseek.com/chat/completions'//定义 API 的端点 URL。
        const headers = {
            'Content-Type': 'application/json',
            "Authorization": `Bearer ${import.meta.env.VITE_DEEPSEEK_API_KEY}`
        }

        try {
            const response = await fetch(endpoint, {//发送post请求
                method: 'POST',
                headers: headers,
                body: JSON.stringify({
                    model: "deepseek-chat",
                    messages: [{ role: "user", content: question }],
                    stream: streaming
                })//将对象转换成json字符串
            })

            if (!response.ok) {
                const errorData = await response.json()
                setContent(`请求出错: ${errorData.message || '未知错误'}`)
                return
            }

            // 流式输出
            if (streaming) {
                const reader = response.body.getReader() // 读取器用来读取响应体二进制数据
                const decoder = new TextDecoder() // 创建一个解码器  用于将二进制数据解码为字符串
                //解码长度取决于存储系统的位数，64位表示一次性只能解码64位，所以会分开解码
                let isDecodingComplete = false // 表示解码是否完成
                let buffer = ''//缓冲区字符串 ，用于存储未处理的剩余数据

                while (!isDecodingComplete) {//循环读取响应体的数据，直到解码完成，因为一次性只能解码64位，电脑操作系统为64位
                    const { value, done: chunkDone } = await reader.read()//读取二进制数据
                    //value表示读取到的二进制数据，done为布尔值，指示流是否结束 （进行了重命名）
                    const chunkValue = buffer + decoder.decode(value)//decoder.decode(value)为字符串类型
                    //将缓冲区内容与新解码的数据块拼接。这是关键步骤，因为网络数据包可能在任何字节处分隔。
                    //详解步骤：
                    //decoder.decode(value)为解码的字符串，有可能完整有可能只有前半段或中间值，或后半段
                    //buffer + decoder.decode(value)将之前缓存区的数据和新的解码数据进行拼接
                    //再拿去下面处理，进行分割去头，如果拿到了完整的数据，则展示，如果数据不完整，
                    //则则存入缓冲区，与下一个新数据进行拼接
                    // const remaining = "data: {\"text\":\"你好，世界" // 不完整，全部剩余
                    // buffer = remaining // 存入缓冲区
                    //chunkValue = "data: {\"text\":\"你好，世界" + "！"}\n\ndata: {\"text\":\"流式传输"
                    //再分割：
                    //[
                    // "data: {\"text\":\"你好，世界！\"}",  // 完整JSON
                    //     "",                                  // 空行
                    //     "data: {\"text\":\"流式传输"         // 不完整
                    //]
                    //第一条完整：解析显示"你好，世界！"
                    // 第三条不完整：存入缓冲区 buffer = "data: {\"text\":\"流式传输"
                    buffer = ''
                    //清理缓冲区，准备处理新数据
                    const lines = chunkValue.split('\n').filter(line => line.startsWith('data:')) //过滤以data:开头的行
                    //split('\n')按换行符分割数据，filter()只保留以data:开头的有效数据行
                    const remaining = chunkValue.split('\n').filter(line => !line.startsWith('data:')).join('\n')//过滤不以data:开头的行，并拼接成字符串
                    //提取所有非数据行（通常指部分行或不完整数据），这些内容需要保存到缓存区

                    if (remaining) {
                        buffer = remaining//如果有不完整数据，存入缓存区等待下次读取
                    }

                    for (const line of lines) {
                        const incoming = line.slice(6)//移除每行开头的data:前缀（6个字符），获取有效的json数据
                        if (incoming === '[DONE]') {
                            //这段代码是在处理从服务器流式返回的数据，当遇到特定标记 [DONE] 时，表示数据传输结束，将停止解码过程。
                            isDecodingComplete = true
                            break
                        }
                        try {
                            const data = JSON.parse(incoming)//将字符串解析为json对象
                            //incoming的结构：
                            // {
                            //     choices: [
                            //         {
                            //             delta: {
                            //                 content: "这是部分内容"
                            //             }
                            //         }
                            //     ]
                            //   }
                            const delta = data.choices[0].delta.content//依次提取choices数组中的每个元素内的内容
                            if (delta) {
                                setContent((prev) => prev + delta)
                            }
                        } catch (err) {
                            console.log(err)
                        }
                    }
                }
            } else {
                const data = await response.json()
                setContent(data.choices[0].message.content)
            }
        } catch (error) {
            setContent(`发生错误: ${error.message}`)
        }
    }

    return (
        <div className="container">
            <div>
                <label htmlFor="">请输入:</label>
                <input type="text" className="input" onChange={(e) => {
                    setQuestion(e.target.value)
                }} />
                <button onClick={update}>发送</button>
            </div>
            <div className="response">
                <div>
                    <label htmlFor="">streaming</label>
                    <input type="checkbox" onChange={(e) => {
                        setStreaming(e.target.checked)
                    }} />
                </div>
                <div>{content}</div>
            </div>
        </div>
    )
}

export default Deepseek